
package botlab.gui;

import botlab.Game;
import botlab.GameManager;
import botlab.compiler.MyCompiler;
import botlab.gui.actionlistener.*;
import botlab.gui.levelchooser.LevelChooserPanel;
import botlab.gui.library.LibraryFrame;
import botlab.gui.levelchooser.LevelChooserPanel;
import com.Ostermiller.Syntax.HighlightedDocument;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicTabbedPaneUI;

/**
 *
 * @author Quentin
 */
public class GUI
{
    private HashMap<String,ImageIcon> icons;
    private LinkedHashMap<String,JButton> buttons; // LinkedHashMap keeps order
    private HashMap<JButton,ActionListener> actions;
    
    private JFrame frame;
    private JTabbedPane tabbedPane;
    
    // three states : editor, levelChooser or render
    private Container editorPanel;
    private Container levelChooserPanel;
    private Container renderPanel;
    private JPanel levelChooser;
    
    // editor components
    private HighlightedDocument hDoc;
    private JTextPane editorPane;
    private JTextArea errorsArea;
    private JTextAreaWriter errorsWriter;
    
    private JLabel notifications;
    
    // render components
    private Canvas renderCanvas;
    
    // help frame
    LibraryFrame helpFrame;
    
    
    public static final int LEVEL_CHOOSER = 0;
    public static final int EDITOR = 1;
    public static final int RENDER = 2;
    
    
    
    public GUI()
    {
        icons = new HashMap<String,ImageIcon>();
        buttons = new LinkedHashMap<String,JButton>();
        actions = new HashMap<JButton,ActionListener>();
        
        // We start the game even if it's not shown
        final Game game = GameManager.getInstance().getGame();
        game.startCanvas(true); // wait for complete loading
        game.enqueue(new Callable<Void>()
        {
            public Void call()
            {
                game.getFlyByCamera().setDragToRotate(true);
                return null;
            }
        });
        
        createFrame();
        helpFrame = new LibraryFrame(this);
        createMenu();
        createPanels();
        
        setMode(LEVEL_CHOOSER);
        Notifier.getInstance().enterState("home");
        
        frame.add(tabbedPane,BorderLayout.CENTER);
        frame.add(notifications,BorderLayout.SOUTH);
        frame.setLocation(50, 50);
        frame.pack();
        frame.setVisible(true);
         
    }
    
    private void createFrame()
    {
        JPopupMenu.setDefaultLightWeightPopupEnabled(false);
        frame = new JFrame("Botlab project");
        frame.setIconImage(new ImageIcon(GUI.class.getResource("/icons/botlab.png")).getImage());
        frame.setSize(300,200);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter(){
            @Override
            public void windowClosed(WindowEvent e) {
                GameManager.getInstance().getGame().stop(true);
            }
        });
        setSystemLookAndFeel();
    }
    
    private void createPanels()
    {
        notifications = new JLabel();
        Notifier.getInstance().setNotifications(notifications);
        tabbedPane = new JTabbedPane();
        
        /* The JFileChooser window won't be shown (and will crash) 
         * if the canvas is removed from the JFrame,
         * so we use a JTabbedPane that will always contain the canvas and be 
         * contained by the JFrame. However, we don't want the user to be able 
         * to switch between tabs. That why we hide them with the folowing hack.
         */
        tabbedPane.setUI(new BasicTabbedPaneUI() {    
            @Override
            protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight){  return 0;  }  
            @Override
            protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect){}
        });
        
        this.renderPanel=createRenderPanel();
        this.editorPanel=createEditorPanel();
        this.levelChooserPanel=createLevelChooserPanel();
        
        tabbedPane.addTab("LevelChooser",levelChooserPanel);
        tabbedPane.addTab("Editor",editorPanel);
        tabbedPane.addTab("Render", renderPanel);
    }
    
    public void setMode(int index)
    {
        tabbedPane.setSelectedIndex(index);
        switch(index)
        {
            case GUI.LEVEL_CHOOSER:
                buttons.get("open").setEnabled(false);
                buttons.get("save").setEnabled(false);
                buttons.get("start").setEnabled(false);
                buttons.get("stop").setEnabled(false);
                buttons.get("choose level").setEnabled(false);
                buttons.get("help").setEnabled(true);
                break;
            case GUI.EDITOR:
                buttons.get("open").setEnabled(true);
                buttons.get("save").setEnabled(true);
                buttons.get("start").setEnabled(true);
                buttons.get("stop").setEnabled(false);
                buttons.get("choose level").setEnabled(true);
                buttons.get("help").setEnabled(true);
                break;
            case GUI.RENDER:
                buttons.get("open").setEnabled(false);
                buttons.get("save").setEnabled(false);
                buttons.get("start").setEnabled(false);
                buttons.get("stop").setEnabled(true);
                buttons.get("choose level").setEnabled(false);
                buttons.get("help").setEnabled(false);
                break;
            default:
                break;
        }
    }
    
    private Container createLevelChooserPanel()
    {
        return new LevelChooserPanel(this);
    }
    
    private Container createEditorPanel()
    {
        // We init the syntax colorer
        hDoc = new HighlightedDocument();
        hDoc.setHighlightStyle(HighlightedDocument.JAVA_STYLE);
        
        // We create a JTextPane with syntax coloration
        editorPane = new JTextPane(hDoc);
        
        // The compilation outputs will be written on the JTextArea.
        errorsArea = new JTextArea();
        errorsArea.setLineWrap(true);
        errorsArea.setEditable(false);
        errorsWriter = new JTextAreaWriter(errorsArea);
        MyCompiler.setErrPrintWriter(new PrintWriter(errorsWriter));
        
        // We encapsulate both editorPane and errorsArea in a JScrollPane 
        JScrollPane jsp1 = new JScrollPane(editorPane);
        jsp1.setPreferredSize(new Dimension(640,380));
        JScrollPane jsp2 = new JScrollPane(errorsArea);
        jsp2.setPreferredSize(new Dimension(640,100));
        
        // Finally, we separate them with a JSplitPane 
        JSplitPane jsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
                jsp1,
                jsp2);
        
        return jsp;
    }
    
    private Container createRenderPanel()
    {
        JPanel p = new JPanel();
        p.setLayout(new BorderLayout());
        renderCanvas = GameManager.getInstance().getCanvas();
        p.add(renderCanvas,BorderLayout.CENTER);
        return p;
    }
    
    private void createMenu()
    {
        JPanel menu = new JPanel();
        menu.setLayout(new BoxLayout(menu, BoxLayout.Y_AXIS));
        menu.add(createToolBar());
        this.frame.add(menu,BorderLayout.NORTH);
    }
    
    private void setSystemLookAndFeel()
    {
        try
        {
            UIManager.setLookAndFeel(
                    UIManager.getSystemLookAndFeelClassName());
        } catch (Exception t)
        {
            t.printStackTrace();
        }
        SwingUtilities.updateComponentTreeUI(frame);
    }
    
    private void loadIcon(String name)
    {
        icons.put(name,new ImageIcon(getClass().getResource(
                "/icons/"+name+".png")));
    }
    
    private void createButton(String name, ActionListener al)
    {
        loadIcon(name);
        JButton b = new JButton(icons.get(name));
        b.setToolTipText(name);
        b.setBorderPainted(false);
        b.setFocusPainted(false);
        buttons.put(name,b);
        actions.put(b, al);
    }
    
    private void createButtons()
    {
        createButton("quit",new QuitActionListener());
        createButton("open",new OpenActionListener(this));
        createButton("save",new SaveActionListener(this));
        createButton("start",new StartActionListener(this));
        createButton("stop",new StopActionListener(this));
        createButton("choose level",new LevelActionListener(this));
        createButton("help",new LibActionListener(this));
        
        initButtons();
    }
    
    private void initButtons()
    {
        for(Iterator<Entry<JButton, ActionListener>> it = actions.entrySet().iterator();it.hasNext();)
        {
            Entry<JButton,ActionListener> es = it.next();
            es.getKey().addActionListener(es.getValue());
        }
    }
    
    private JToolBar createToolBar()
    {
        createButtons();
        JToolBar mainToolBar = new JToolBar();
        mainToolBar.setFloatable(false);
        for(Iterator<Entry<String, JButton>> it = buttons.entrySet().iterator();it.hasNext();)
        {
            mainToolBar.add(it.next().getValue());
        }
        mainToolBar.setAlignmentX(0);
        return mainToolBar;
    }

    public HashMap<String, ImageIcon> getIcons()
    {
        return icons;
    }

    public LinkedHashMap<String, JButton> getButtons()
    {
        return buttons;
    }

    public HashMap<JButton, ActionListener> getActions()
    {
        return actions;
    }

    public JFrame getFrame()
    {
        return frame;
    }

    public Container getEditorPanel()
    {
        return editorPanel;
    }

    public Container getRenderPanel()
    {
        return renderPanel;
    }

    public HighlightedDocument gethDoc()
    {
        return hDoc;
    }

    public JTextPane getEditorPane()
    {
        return editorPane;
    }

    public JTextArea getErrorsArea()
    {
        return errorsArea;
    }

    public JTextAreaWriter getErrorsWriter()
    {
        return errorsWriter;
    }

    public Canvas getRenderCanvas()
    {
        return renderCanvas;
    }

    public JTabbedPane getTabbedPane()
    {
        return tabbedPane;
    }

    public JLabel getNotifications()
    {
        return notifications;
    }

    public LibraryFrame getHelpFrame()
    {
        return helpFrame;
    }

    
    
    
    
    
    
    
    public static void main(String[] args)
    {
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                try
                {
                    new GUI();
                }
                catch(Exception e)
                {
                    e.printStackTrace();
                    JOptionPane.showMessageDialog(null,
                    "An error occured during application loading."+
                    "\n Please ensure jre7 is installed and openGL2 is supported by your graphic card."+
                    "\nException detail :\n"+
                    e.toString(),
                    "Loading error",
                    JOptionPane.ERROR_MESSAGE);
                    
                }
            }
        });
    }
    
    
}
